// 抖音 cookie 的获取、验证、缓存

import { log, userAgent } from "../share/common.js";
import { SizeHint, unload, Webview } from "/deno.land/x/webview/mod.ts";

const DEFAULT_URL = "https://www.douyin.com/";

// 一些固定的常数
export const Keys = {
  AcNonce: "__ac_nonce",
  AcSignature: "__ac_signature",
  AcReferer: "__ac_referer",
};

// 默认的缓存 cookie 值的文件
export const defaultCookieFile = "douyin-cookie.txt";

/**
 * 获取本地缓存的 cookie 值。
 * @param cookieFile 保存cookie值的文件
 */
export async function getCache(
  cookieFile: string,
): Promise<string> {
  return await Deno.readTextFile(cookieFile);
}

/**
 * 判断 cookie 值是否真实效。
 * @param cookie 要判断的 cookie 值
 */
export async function isValid(cookie: string): Promise<boolean> {
  // 初步的格式判断
  if (cookie.length === 0 || !(cookie.includes(`${Keys.AcNonce}=`) && cookie.includes(`${Keys.AcSignature}=`))) {
    log(`cookie 值的格式不符合要求，必须至少包含 ${Keys.AcNonce} 和 ${Keys.AcSignature}！`);
    return false;
  }

  // 通过请求页面看返回的结果是否 OK 来判断 cookie 是否真实有效
  // 如果页面返回的响应头 Set-Cookie 中包含 __ac_nonce 值，则为无效
  const response = await fetch(DEFAULT_URL, {
    headers: {
      "Host": "www.douyin.com",
      "Referer": "https://www.douyin.com/",
      "User-Agent": userAgent,
      "Cookie": cookie,
    },
  });
  if (response.headers.has("Set-Cookie")) {
    const setCookie = response.headers.get("Set-Cookie");
    // console.log(`isValid - response.set-cookie=${setCookie}`);
    await response.body?.cancel();
    return !!setCookie && setCookie.includes("ttwid=");
  } else return false;
}

/**
 * 生成一个简单有效的抖音 cookie。
 * 通过打开 webview 加载抖音主页并返回主页的 document.cookie 中用于认证的有效 cookie 值。
 * @return 有效的 cookie 值，格式为 `__ac_nonce=...; __ac_signature=...; __ac_referer=...`
 */
export async function create(unloadResource = true): Promise<string> {
  let cookie = "";
  const webview = new Webview(
    true,
    // 设置窗口大小
    {
      width: 1300,
      height: 1200,
      hint: SizeHint.NONE,
    },
  );

  // 设置为打开抖音主页
  webview.navigate(DEFAULT_URL);

  // 设置窗口标题
  webview.title = "获取抖音 cookie - 页面出来后手动关闭本窗口即可继续 @RJ";

  // 绑定用于接收 init 注入代码返回数据的函数
  webview.bind("rj_get_value", (documentCookie: string) => {
    // documentCookie 为 document.cookie 的原始值。
    // 解析出必须的 cookie 值并缓存起来
    if (documentCookie) {
      // console.log(`documentCookie=${documentCookie}`);
      // const v1 = ("; " + documentCookie).split(`; ${Keys.AcNonce}=`).pop()?.split(";")[0];
      // const v2 = ("; " + documentCookie).split(`; ${Keys.AcSignature}=`).pop()?.split(";")[0];
      // const v3 = ("; " + documentCookie).split(`; ${Keys.AcReferer}=`).pop()?.split(";")[0];
      // cookie = `${Keys.AcNonce}=${v1}; ${Keys.AcSignature}=${v2}; ${Keys.AcReferer}=${v3}`;
      cookie = documentCookie;
      // console.log(`extracted cookie=${cookie}`);
    }
  });

  // 注入 js 代码到页面内，页面初始化时执行。
  // RJ 2023-01-15 实测，会先有两次 about:blank 页面的 init 再有对应 url 的页面的 init，
  // 故判断一下页面 url 再传值，避免重复调用。
  webview.init(`
    const rj_url = "${DEFAULT_URL}"
    console.log("DEFAULT_URL=" + rj_url + ", location.href=" + location.href)
    if (location.href === rj_url) {
      console.log("call rj_get_value to return cookie")
      // 封装数据返回给 webview
      rj_get_value(document.cookie)
    }
  `);

  // 运行并显示界面，并等待用户关闭
  webview.run();

  // 销毁所有 webview 所加载的资源
  if (unloadResource) unload();

  // 返回 cookie
  if (!cookie) throw Error("本次没有获取到有效的 cookie 值，重新重试一次通常能解决问题！");
  return await Promise.resolve(cookie);
}
